package com.hemerasolutions.v2trainer.model.base;

import java.lang.reflect.Field;
import java.util.List;
import java.util.UUID;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;

import com.hemerasolutions.v2trainer.model.interfaces.BaseModelInterface;
import com.hemerasolutions.v2trainer.util.annotations.ReferencedModel;

@MappedSuperclass
public abstract class BaseModel implements BaseModelInterface {

	private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Integer id;

    @Transient
    protected Long uuid;
	
	public BaseModel(){
		this.id = null;
		this.uuid = UUID.randomUUID().getMostSignificantBits();
	}

	
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	@Override
	public <T> boolean isListEquals(List<T> orig, List<T> other) {
		
		int x = 0;
		
		if(orig.size() != other.size()){
			return false;
		}
		
		for(; x < orig.size();x++){
			if(((BaseModelInterface)orig.get(x)).hasChanged(other.get(x))){
				return false;
			}
		}
		
		return true;
	}

	
	public boolean hasChanged(Object other){
		if(this.equals(other)){
			if(this.isListsEquals(this,other)){
				return false;
			}else{
				return true;
			}
		}else{
			return true;
		}	
	}

	private boolean isListsEquals(Object orig, Object other) {
		
		for(Field field: other.getClass().getDeclaredFields()){
			if(field.getType().equals(List.class)){
				try {
					
					field.setAccessible(true);
					
					@SuppressWarnings("rawtypes")
					List itensOrig = (java.util.List)field.get(orig);
					@SuppressWarnings("rawtypes")
					List itensOther = (java.util.List)field.get(other);
					
					if(field.isAnnotationPresent(ReferencedModel.class)){
						if(! field.getAnnotation(ReferencedModel.class).compareOnChange()){
							return true;
						}
					}
					
						
					if(itensOrig.size() != itensOther.size()){
						return false;
					}else{
						for(int x = 0; x < itensOrig.size();x++){
							if(itensOrig.get(x) instanceof BaseModelInterface ){
								if(((BaseModelInterface)itensOrig.get(x)).hasChanged(itensOther.get(x))){
									return false;
								}
							}else{
								if(! itensOrig.get(x).equals(itensOther.get(x))){
									return false;
								}
							}
						}
					}
					
				
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
		
		return true;
	}


	public Long getUuid() {
		if(uuid == null){
			uuid = UUID.randomUUID().getMostSignificantBits();
		}
		return uuid;
	}


	public void setUuid(Long uuid) {
		this.uuid = uuid;
	}


	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
		return result;
	}


	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		BaseModel other = (BaseModel) obj;
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (uuid == null) {
			if (other.uuid != null) {
				return false;
			}
		} else if (!uuid.equals(other.uuid)) {
			return false;
		}
		return true;
	}


	@Override
	public String toString() {
		return "BaseModel [id=" + id + ", uuid=" + uuid + "]";
	}

	
	
	
}
